#include "Grid.h"

#include <vector>

#include "Vertex.h"

namespace D3D10Utils
{
	Grid::Grid(const UINT rows, const UINT columns)
		: mDevice(0)
		, mVertexBuffer(0)
		, mIndexBuffer(0)
		, mNumVertices(rows * columns)
		, mNumFaces((rows - 1) * (columns - 1) * 2)
		, mRows(rows)
		, mColumns(columns)
	{

	}

	Grid::~Grid()
	{
		ReleaseCOM(mVertexBuffer);
		ReleaseCOM(mIndexBuffer);
	}

	void Grid::init(ID3D10Device * const device, const float cellSpacingX, 
		const float cellSpacingZ)
	{
		if (!device)
		{
			MessageBox(0, L"NULL pointer", 0, 0);
			PostQuitMessage(0);
		}

		mDevice = device;

		// Compute the half width and height of the grid.		
		// Our grid will be at xz plane, that the reason why we ask for cell
		// spacing in that coordinates.
		const float halfWidth = (mColumns - 1) * cellSpacingX * 0.5f;
		const float halfHeight = (mRows - 1) * cellSpacingZ * 0.5f;
		
		// Create vertices:
		// Vij = (-0.5 * width + i * cellSpacingX, 0.5 * height - j * cellSpacingZ)
		Vertex *vertices = new Vertex[mNumVertices];
		for(UINT j = 0; j < mColumns; ++j)
		{
			const float x = -halfWidth + j * cellSpacingX;
			
			for(UINT i = 0; i < mRows; ++i)
			{
				const float z = halfHeight - i * cellSpacingZ;
				const float y = getHeight(x, z);

				// Update the vertex
				const UINT vertexIndex = i * mRows + j;
				vertices[vertexIndex].pos = D3DXVECTOR3(x, y, z);

				// Color the vertex based on its height.
				if( y < -10.0f )
				{
					vertices[vertexIndex].diffuse = BEACH_SAND;
					vertices[vertexIndex].spec = D3DXCOLOR(0.2f, 0.2f, 0.2f, 32.0f);
				}
				else if( y < 5.0f )
				{
					vertices[vertexIndex].diffuse = LIGHT_YELLOW_GREEN;
					vertices[vertexIndex].spec = D3DXCOLOR(0.2f, 0.2f, 0.2f, 32.0f);
				}
				else if( y < 12.0f )
				{
					vertices[vertexIndex].diffuse = DARK_YELLOW_GREEN;
					vertices[vertexIndex].spec = D3DXCOLOR(0.2f, 0.2f, 0.2f, 32.0f);
				}
				else if( y < 20.0f )
				{
					vertices[vertexIndex].diffuse = DARKBROWN;
					vertices[vertexIndex].spec = D3DXCOLOR(0.4f, 0.4f, 0.4f, 64.0f);
				}
				else
				{
					vertices[vertexIndex].diffuse = WHITE;
					vertices[vertexIndex].spec = D3DXCOLOR(0.6f, 0.6f, 0.6f, 64.0f);
				}

				// n = (-df/dx, 1, -df/dz)
				D3DXVECTOR3 normal;
				normal.x = -0.03f * z * cosf(0.1f * x) - 0.3f * cosf(0.1f * z);
				normal.y = 1.0f;
				normal.z = -0.3f * sinf(0.1f * x) + 0.03f * x * sinf(0.1f * z);
				D3DXVec3Normalize(&vertices[vertexIndex].normal, &normal);
			}
		}
		
		// Create indices.
		DWORD *indices = new DWORD[mNumFaces * 3];
		UINT quadOffset = 0;
		for(UINT j = 0; j < mColumns - 1; ++j)
		{
			for(UINT i = 0; i < mRows - 1; ++i)
			{
				// Define the needed vertices.
				const DWORD upperLeft = i * mRows + j;
				const DWORD upperRight = i * mRows + j + 1;
				const DWORD bottomLeft = (i + 1) * mRows + j;
				const DWORD bottomRight = (i + 1) * mRows + j + 1;

				indices[quadOffset] = upperLeft;
				indices[quadOffset + 1] = upperRight;
				indices[quadOffset + 2] = bottomLeft;

				indices[quadOffset + 3] = bottomLeft;
				indices[quadOffset + 4] = upperRight;
				indices[quadOffset + 5] = bottomRight;

				// Next quad
				quadOffset += 6; 
			}
		}

		// Input:
		// 1. An array of vertices. Each vertex has a position component
		// and a normal component.
		// 2. An array of indices.

		// For each triangle in the mesh.
		const DWORD numTriangles = (mNumFaces % 2 == 0) ? mNumFaces / 2 : (mNumFaces - 1) / 2 + 1; 
		for (DWORD i = 0; i < numTriangles; ++i)
		{
			// indices of the ith triangle.
			DWORD i0 = indices[i * 3 + 0];
			DWORD i1 = indices[i * 3 + 1];
			DWORD i2 = indices[i * 3 + 2];
		
			// vertices of ith triangle.
			Vertex v0 = vertices[i0];
			Vertex v1 = vertices[i1];
			Vertex v2 = vertices[i2];

			// compute face normal.
			D3DXVECTOR3 e0 = v1.pos - v0.pos;
			D3DXVECTOR3 e1 = v2.pos - v0.pos;
			D3DXVECTOR3 faceNormal;
			D3DXVec3Cross(&faceNormal, &e0, &e1);

			// This triangle shares the following three vertices,
			// so add this face normal into the average of these
			// vertex normals.
			vertices[i0].normal += faceNormal;
			vertices[i1].normal += faceNormal;
			vertices[i2].normal += faceNormal;
		}

		// For each vertex v, we have sumed the face normals of all 
		// the triangles that share v, so now we just need to normalize.
		for (DWORD i = 0; i < mNumVertices; ++i)
		{
			D3DXVec3Normalize(&vertices[i].normal, &vertices[i].normal);
		}

		// Description of the vertex buffer.
		D3D10_BUFFER_DESC vertexBufferDescription;
		vertexBufferDescription.ByteWidth = sizeof(Vertex) * mNumVertices;

		// A resource that can only be read by the GPU. 
		// It cannot be written by the GPU, and cannot be accessed at all by the CPU. 
		// This type of resource must be initialized when it is created, 
		// since it cannot be changed after creation.
		vertexBufferDescription.Usage = D3D10_USAGE_IMMUTABLE; 

		// Bind a buffer as a vertex buffer to the input-assembler stage.
		vertexBufferDescription.BindFlags = D3D10_BIND_VERTEX_BUFFER; 
		vertexBufferDescription.CPUAccessFlags = 0; // No CPU access necessary.
		vertexBufferDescription.MiscFlags = 0; 	
		// Specifies data for initializing a subresource.
		D3D10_SUBRESOURCE_DATA vertexBufferData;
		vertexBufferData.pSysMem = vertices;

		ErrorHandler(mDevice->CreateBuffer(&vertexBufferDescription, &vertexBufferData, &mVertexBuffer));

		// Description of the index buffer.
		D3D10_BUFFER_DESC indexBufferDescription;
		indexBufferDescription.ByteWidth = sizeof(DWORD) * mNumFaces * 3;

		// A resource that can only be read by the GPU. 
		// It cannot be written by the GPU, and cannot be accessed at all by the CPU. 
		// This type of resource must be initialized when it is created, 
		// since it cannot be changed after creation.
		indexBufferDescription.Usage = D3D10_USAGE_IMMUTABLE;

		// Bind a buffer as a index buffer to the input-assembler stage.
		indexBufferDescription.BindFlags = D3D10_BIND_INDEX_BUFFER;
		indexBufferDescription.CPUAccessFlags = 0; // No CPU access necessary.
		indexBufferDescription.MiscFlags = 0;

		D3D10_SUBRESOURCE_DATA indexBufferData;
		indexBufferData.pSysMem = indices;

		ErrorHandler(mDevice->CreateBuffer(&indexBufferDescription, &indexBufferData, &mIndexBuffer));
		
		delete[] vertices;
		delete[] indices;
	}

	void Grid::draw()
	{
		const UINT stride = sizeof(Vertex);
		const UINT offset = 0;
		mDevice->IASetVertexBuffers(0, 1, &mVertexBuffer, &stride, &offset);
		mDevice->IASetIndexBuffer(mIndexBuffer, DXGI_FORMAT_R32_UINT, 0);

		mDevice->DrawIndexed(mNumFaces * 3, 0, 0);
	}
}